package io.renren.modules.media.support;

import com.google.common.collect.Maps;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * @program: RTC
 * @description: 核心处理接口类
 **/
@Component
public class MinioSupport {

    @Value("${minio.bucket}")
    private String bucketName;
    @Autowired
    private MinioClient minioClient;

    /**
     * 创建一个桶
     *
     * @param bucket Bucket name
     * @throws Exception
     */
    public void createBucket(String bucket) throws Exception {
        boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        if (!found) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }
    }

    /**
     * 上传一个文件
     *
     * @param stream     文件流
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param tag
     * @throws Exception
     */
    public void uploadFile(InputStream stream, String bucketName, String objectName, Map<String, String> tag) throws Exception {
        createBucket(bucketName);
        PutObjectArgs args = PutObjectArgs
                .builder()
                .bucket(bucketName)
                .stream(stream, -1, 10485760)
                .object(URLDecoder.decode(objectName, "utf-8"))
                .headers(tag)
                .build();
        minioClient.putObject(args);
    }

    /**
     * 列出所有的桶
     *
     * @return
     * @throws Exception
     */
    public List<String> listBuckets() throws Exception {
        List<Bucket> list = minioClient.listBuckets();
        List<String> names = new ArrayList<>();
        list.forEach(b -> {
            names.add(b.name());
        });
        return names;
    }

    /**
     * 列出一个桶中的所有文件和目录
     *
     * @param bucket
     * @return
     * @throws Exception
     */
    public List<Map<String, String>> listFiles(String bucket) throws Exception {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucket).recursive(true).build());

        List<Map<String, String>> infos = new ArrayList<>();
        results.forEach(r -> {
            Map<String, String> map = new HashMap<>();
            try {
                Item item = r.get();
                map.put("filename", item.objectName());
                map.put("directory", item.isDir() + "");
                infos.add(map);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return infos;
    }

    /**
     * 下载一个文件
     *
     * @param bucket
     * @param objectName
     * @return
     * @throws Exception
     */
    public InputStream download(String bucket, String objectName) throws Exception {
        GetObjectArgs args = GetObjectArgs
                .builder()
                .bucket(bucket)
                .object(URLDecoder.decode(objectName, "utf-8"))
                .build();
        InputStream stream = minioClient.getObject(args);
        return stream;
    }

    /**
     * 删除一个桶
     *
     * @param bucket
     * @throws Exception
     */
    public void deleteBucket(String bucket) throws Exception {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucket).build());
    }

    /**
     * 删除一个对象
     *
     * @param bucket
     * @param objectName
     * @throws Exception
     */
    public void deleteObject(String bucket, String objectName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(URLDecoder.decode(objectName, "utf-8")).build());
    }

    /**
     * 获取文件描述信息
     *
     * @param path
     * @return
     */
    public StatObjectResponse getMetadata(Path path, String bucket) throws Exception {
        try {
            StatObjectArgs args = StatObjectArgs.builder()
                    .bucket(bucket)
                    .object(path.toString())
                    .build();
            return minioClient.statObject(args);
        } catch (Exception e) {
            throw new Exception("获取文件返回信息", e);
        }
    }

    /**
     * 获取文件描述信息
     *
     * @param paths
     * @param bucket
     * @return
     */
    public Map<Path, StatObjectResponse> getMetadata(Iterable<Path> paths, String bucket) {
        return StreamSupport.stream(paths.spliterator(), false)
                .map(path -> {
                    try {
                        StatObjectArgs args = StatObjectArgs.builder()
                                .bucket(bucket)
                                .object(path.toString())
                                .build();
                        return new HashMap.SimpleEntry<>(path, minioClient.statObject(args));
                    } catch (Exception e) {
                        throw new RuntimeException("获取文件描述信息错误", e);
                    }
                })
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    /**
     * @param source      带有对象前缀的路径。必须包含对象名称
     * @param stream      文件流
     * @param bucketName  bucket名称
     * @param contentType 对象的MIME类型
     * @param headers     要放置在文件上的其他标签。map必须是可变的
     * @throws Exception
     */
    public void upload(Path source, InputStream stream, String bucketName, String contentType, Map<String, String> headers) throws Exception {
        try {
            createBucket(bucketName);
            PutObjectArgs args = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(URLDecoder.decode(source.toString(), "utf-8"))
                    .stream(stream, stream.available(), -1)
                    .headers(headers)
                    .contentType(contentType)
                    .build();

            minioClient.putObject(args);
        } catch (Exception e) {
            throw new Exception("上传失败", e);
        }
    }

    /**
     * 预览地址
     *
     * @param bucket
     * @param objectName 存储路径
     * @param expiry
     * @return
     */
    public String previewAddress(String bucket, String objectName, Integer expiry) {
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                .bucket(bucket)
                .object(objectName)
                .expiry(expiry)
                .method(Method.GET)
                .build();
        try {
            return minioClient.getPresignedObjectUrl(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 下载地址
     *
     * @param bucket
     * @param objectName 存储路径
     * @param expiry
     * @return
     */
    public String downloadAddress(String bucket, String objectName, Integer expiry) {
        Map<String, String> reqParams = new HashMap<String, String>();
        reqParams.put("response-content-type", "application/octet-stream");
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                .bucket(bucket)
                .object(objectName)
                .expiry(expiry)
                .method(Method.GET)
                .extraQueryParams(reqParams)
                .build();
        try {
            return minioClient.getPresignedObjectUrl(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 预览地址
     *
     * @param bucket
     * @param objectNames
     * @param expiry
     * @return
     */
    public Map<String, String> previewAddress(String bucket, List<String> objectNames, Integer expiry) {

        Map<String, String> map = Maps.newHashMap();
        for (String objectName : objectNames) {
            String url = previewAddress(bucket, objectName, expiry);
            map.put(objectName, url);
        }
        return map;
    }

    /**
     * 上传文件
     *
     * @param bucketName  bucket名称
     * @param objectName  文件名称
     * @param stream      文件流
     * @param size        大小
     * @param contentType 类型
     * @throws Exception
     */
    public void uploadObject(String bucketName, String objectName, InputStream stream, long size, String contentType, Map<String, String> headers) throws Exception {
        try {
            createBucket(bucketName);
            PutObjectArgs args = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(URLDecoder.decode(objectName, "utf-8"))
                    .stream(stream, size, -1)
                    .contentType(contentType)
                    .headers(headers)
                    .build();
            minioClient.putObject(args);
        } catch (Exception e) {
            throw new Exception("上传失败", e);
        }
    }

    /**
     * 获取url地址
     *
     * @param fileName
     * @param expiry
     * @return
     */
    public String getUrl(String fileName, Integer expiry) {
        return getUrl(fileName, expiry,null);
    }

    public String getUrl(String fileName, Integer expiry, String type) {
        String url;
        if(StringUtils.isNotBlank(type) && type.equals("download")){
            url = downloadAddress(bucketName, fileName, expiry);
        }else{
            url = previewAddress(bucketName, fileName, expiry);
        }
        return url;
    }
}
